/* *****************************************************************************
 *  Garage Band: Cheeseburger in Paradise
 *
 *  CheeseBurgerParadise.h
 *
 *  Based on MotionViewer
 *
 * *****************************************************************************
 */
#include <iostream>
#include <ogreplatform.h>
#include <ogreplatform.h>
#if OGRE_PLATFORM == OGRE_PLATFORM_APPLE
#include <Carbon/Carbon.h>
#endif


#include "CheeseBurgerParadise.h"
#include "ViconVVisualizer.h"
#include "MocapVisualizer.h"
#include "ViconCharacter.h"
#include "ViconMotionDataSet.h"
#include "ConfigFile.h"
#include "Music.h"

CheeseBurgerParadise::CheeseBurgerParadise(): MotionViewerApp()
{
}

CheeseBurgerParadise::~CheeseBurgerParadise()
{
	// delete soundtrack data if created
	if(BandConfigFile::hasSoundtrack()) {
		MusicPlayer::stopSound();
		MusicPlayer::killAllData();
	}
}

void CheeseBurgerParadise::createScene()
{
	// mSceneMgr->setShadowTechnique(SHADOWTYPE_STENCIL_ADDITIVE);
	this->mvScene();
	this->loadTheme();

	//make camera look at center of scesne
	mCamera->lookAt(headNode->getPosition());
	
	// if band enabled, load characters in band
	if ( BandConfigFile::loadBandEnabled() ) {
		this->loadPerformers();
	}

	//location of instruments
	
	// start playing music
	if ( BandConfigFile::hasSoundtrack() ) { 
		MusicPlayer::playSound();
	}
	
	//set camera on autotrack
	//mCamera->setAutoTracking(true, headNode, Vector3(0, 180, 0));

	//this->showDebugOverlay(true);
}
void CheeseBurgerParadise::mvScene(void)
{
	// setup GUI system
	mGUIRenderer = new CEGUI::OgreCEGUIRenderer(mWindow,
	 Ogre::RENDER_QUEUE_OVERLAY, false, 3000, mSceneMgr);

	mGUISystem = new CEGUI::System(mGUIRenderer);

	
	// load scheme and set up defaults
	CEGUI::SchemeManager::getSingleton().loadScheme(
	 (CEGUI::utf8*)"TaharezLookSkin.scheme");
	CEGUI::SchemeManager::getSingleton().loadScheme((CEGUI::utf8*)"motionviewer.scheme");
	mGUISystem->setDefaultMouseCursor(
	 (CEGUI::utf8*)"TaharezLook", (CEGUI::utf8*)"MouseArrow");
	mGUISystem->setDefaultFont((CEGUI::utf8*)"BlueHighway-12");
	CEGUI::MouseCursor::getSingleton().setImage("TaharezLook", "MouseArrow");
	CEGUI::MouseCursor::getSingleton().show( );
	

	headNode = mSceneMgr->getRootSceneNode()->createChildSceneNode();

	// Set ambient light
	mSceneMgr->setAmbientLight(ColourValue(0.5, 0.5, 0.5));

	// Create a light
	/*
	Light* l = mSceneMgr->createLight("MainLight");
	//l->setPosition(6000,5000,6000);
	l->setType(Ogre::Light::LT_DIRECTIONAL);
	l->setDirection(-3, -3, -3);
	l->setDiffuseColour(ColourValue(1.0, 1.0, 1.0));
	
	SceneNode* lightnode = headNode->createChildSceneNode("main_light_node");
	lightnode->attachObject(l);
	l = mSceneMgr->createLight("MainLight2");
	l->setType(Ogre::Light::LT_DIRECTIONAL);
	l->setDirection(3, -4, -1);
	lightnode->attachObject(l);
	*/
	
	Light * l = mSceneMgr->createLight("Light1");
	
	l->setType(Ogre::Light::LT_DIRECTIONAL);
	l->setDiffuseColour(ColourValue(1.0, 1.0, 1.0));
	l->setDirection(-30000, -30000, -30000);
	
	l = mSceneMgr->createLight("Light2");
	l->setType(Ogre::Light::LT_DIRECTIONAL);
	l->setDiffuseColour(ColourValue(1.0, 1.0, 1.0));
	l->setDirection(30000, -30000, 30000);
	
	l = mSceneMgr->createLight("Light3");
	l->setType(Ogre::Light::LT_DIRECTIONAL);
	l->setDiffuseColour(ColourValue(1.0, 1.0, 1.0));
	l->setDirection(0, 10000, 0);

	
	m_pVisualizer = new MocapVisualizer(mSceneMgr, headNode);

	// Adjust Camera
	mCamera->setPosition(0, 180, 440);
	mCamera->lookAt(Ogre::Vector3(0, 10, 0));
	pCameraController = mSceneMgr->getRootSceneNode()->createChildSceneNode("CameraNode");
	pCameraController->attachObject(mCamera);
}

void CheeseBurgerParadise::loadTheme()
{

	//beach terrain
	mSceneMgr->setWorldGeometry("beachterrain.cfg");
	mTerrainRoot = mSceneMgr->getSceneNode("Terrain");
	
	// remove terrain and attach it again to head node
	mSceneMgr->getRootSceneNode()->removeChild(mTerrainRoot);
	headNode->addChild(mTerrainRoot);
	mTerrainRoot->setPosition(Vector3(-2000, 0, -2000));
	

	//create a light on the headnode
	Light *l = mSceneMgr->createLight("HeadNodeLight");
	l->setPosition(headNode->getPosition().x - 40, headNode->getPosition().y + 300, headNode->getPosition().z -40);
	l->setDirection(1,-1,1);
	l->setType(Ogre::Light::LT_DIRECTIONAL);

	//ocean
	this->loadOcean();

	//load sky
	this->loadSky();

	//objects in the scene
	SceneNode * node;
	Entity *ent;
	
	// Load decoration meshes
	// -----------------------
	
	// Surf board
	// -----------
	ent = mSceneMgr->createEntity("SurfBoard", "SurfBoard.mesh");
	node = headNode->createChildSceneNode("SurfBoard");
	node->attachObject(ent);
	//node->setRotation();// rotate(Vector3(1, 0, 0), Radian(-120), Ogre::Node::TransformSpace::TS_WORLD);
	ent->setNormaliseNormals(true);
	node->scale(17,17,17);
	node->translate(Vector3(600, 380, -120));
	node->roll(Radian(90));
	node->pitch(Radian(-90));
	
	// Hay Umbrella
	ent = mSceneMgr->createEntity("HayUmrella", "HayUmbrellaMesh.mesh");
	node = headNode->createChildSceneNode("HayUmbrella");
	node->attachObject(ent);
	ent->setNormaliseNormals(true);
	node->scale(2.5, 2.5, 2.5);
	node->translate(Vector3(-200, 450, -300));
	
	// Beach ball
	ent = mSceneMgr->createEntity("BeachBall", "BeachBall.mesh");
	node = headNode->createChildSceneNode("BeachBall");
	node->attachObject(ent);
	node->scale(3, 3, 3);
	ent->setNormaliseNormals(true);
	node->translate(Vector3(-110, 150, -350));
	
	// percussion
	ent = mSceneMgr->createEntity("Percussion", "Percussion.mesh");
	node = headNode->createChildSceneNode("Percussion");
	node->attachObject(ent);
	node->translate(Vector3(400, 120, 100));
	
	// keyboard
	ent = mSceneMgr->createEntity("Keyboard", "keyboard.mesh");
	node = headNode->createChildSceneNode("Keyboard");
	node->attachObject(ent);
	node->translate(Vector3(0, 105, 0));
	node->yaw( Radian(Degree(180)));

	ent = mSceneMgr->createEntity("palmtrees1", "palmtrees.mesh");
	node = headNode->createChildSceneNode("palmtree1Node");
	node->setPosition(300, 0, -700);
	node->attachObject(ent);
	
	ent = mSceneMgr->createEntity("palmtrees2", "palmtrees.mesh");
	node = headNode->createChildSceneNode("palmtree2Node");
	node->setPosition(-300, 0, -800);
	node->attachObject(ent);
	node->yaw( Radian(Degree(180)));
		
	cbpa = new CBPAnimation();
	cbpa->addAnimatedObjects(mSceneMgr, headNode);
	
}

void CheeseBurgerParadise::loadSky(){
	   /*	char temp[30];
		int skytype = 0;
		int x=1, y=1;
		GetPrivateProfileStringA("Environment", "skytype", "dome",temp, 30,  "OGREBoids.ini");
		if(_stricmp(temp,"box") == 0)
				skytype =1;

		//GetPrivateProfileStringA("Environment", "skypic1", "clearsky.jpg",temp, 30,  "OGREBoids.ini");
			
		x = GetPrivateProfileInt("Environment", "skycurv", 1,  "OGREBoids.ini");
		y = GetPrivateProfileInt("Environment", "skytile", 1,  "OGREBoids.ini");

		GetPrivateProfileStringA("Environment", "skypic1", "clearsky.jpg",temp, 30,  "OGREBoids.ini");
		
		
		if(skytype == 0)
			mSceneMgr->setSkyDome(true, temp, x, y);
		else
			mSceneMgr->setSkyBox(true, temp, 5, 8);
		*/	

	//skybox
	mSceneMgr->setSkyDome(true, "Examples/CloudySky");
	//mSceneMgr->setSkyBox(true, "CheeseBurger/ClearSky" );
	/*
	//sun in the sky (not working)
	SceneNode *flareNode; 
	BillboardSet *flare = mSceneMgr->createBillboardSet("SunFlare",2);
	flare->setMaterialName("LightFlare");
	flareNode = headNode->createChildSceneNode("SunFlare");
	flareNode->attachObject(flare);
	//flareNode->setPosition(mSceneMgr->getLight("MainLight")->getPosition());
	flareNode->setPosition(Vector3(0, 0, 0));
	*/

	//sun (alternative and working)
	ParticleSystem* sunParticle = mSceneMgr->createParticleSystem("Sun", "Space/Sun");
	SceneNode* particleNode = mSceneMgr->getRootSceneNode()->createChildSceneNode("Particle");
	particleNode->attachObject(sunParticle);
	particleNode->translate(Vector3(0, 3000, 0));
}

void CheeseBurgerParadise::loadOcean(){
	
	// Define a plane mesh that will be used for the ocean surface
    Ogre::Plane oceanSurface;
    oceanSurface.normal = Ogre::Vector3::UNIT_Y;
    oceanSurface.d = -5;
    Ogre::MeshManager::getSingleton().createPlane("OceanSurface",
        Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME,
        oceanSurface,
        80000, 80000, 50, 50, true, 1, 1, 1, Ogre::Vector3::UNIT_Z);

    Entity *mOceanSurfaceEnt = mSceneMgr->createEntity( "OceanSurface", "OceanSurface" );
	mOceanSurfaceEnt->setMaterialName("Ocean2_Cg");
	//mOceanSurfaceEnt->setMaterialName("OceanHLSL_GLSL");
    headNode->createChildSceneNode()->attachObject(mOceanSurfaceEnt);

	//if bad frame rate, we can substitute further ocean with a plane
	/*
	Entity *mFarScape = mSceneMgr->createEntity("FarOcean", "OceanSurface");
	SceneNode  *farOceanNode = headNode->createChildSceneNode("FarOcean");
	farOceanNode->attachObject(mFarScape);
	farOceanNode->translate(Vector3(0,120,0));
	farOceanNode->setScale(Vector3(5,1,5));
	*/

}

void CheeseBurgerParadise::loadPerformers()
{
	CharacterList c = BandConfigFile::getCharacters();
		FILE * fp = fopen("gg.txt", "a");
	// loop through all characters in band config file
	for (CharacterList::const_iterator it = c.begin();
		it != c.end(); it++) {
		const CharacterConfig & character = *it;
		fprintf(fp, "Character: %s\n", character.getName().c_str());
		fprintf(fp, "Temporal offset: %f\n\n", character.getDeltaTime());
		

	
		this->loadCharacter( character.getMeshFile().c_str());
		this->loadMocapDataset( character.getName().c_str(), 
			character.getVFile().c_str(), 
			character.getVSKFile().c_str(),
			character.getDeltaTime() );
		
		unsigned int index = m_vCharacters.size() - 1;
		this->connectCharacterWithData(character, index);
		
		// offset character
		m_vCharacters[index].setPosition(character.getOffset().x, character.getOffset().y, character.getOffset().z);
	
	
		// see if character has instrument
		if (character.hasInstrument()) {
			// load instrument with this character
			this->loadCharacter( character.getInstrumentMesh().c_str() );
			this->loadMocapDataset( character.getName().c_str(),
				character.getInstrumentV().c_str(),
				character.getInstrumentVSK().c_str(),
				character.getDeltaTime());
				
			unsigned int instrumentIndex = m_vCharacters.size() - 1;
				
			m_vCharacters[instrumentIndex].connectToMotionData(m_vViconVisualizers[instrumentIndex]->getDataSet());
			m_vCharacters[instrumentIndex].loadSkeletonMapping(character.getInstrumentMap().c_str());

			Vector3 offset = character.getOffset() + character.getInstrumentOffset();
			m_vCharacters[instrumentIndex].setPosition(offset.x, offset.y, offset.z);	
		}	// if hasInstrument()
	}	// for
	
	fclose(fp);
}

void CheeseBurgerParadise::updatePerformers(void)
{
	printBandConfig();


	CharacterList c = BandConfigFile::getCharacters();
	
	// rewind all vicon visualizers
	for (unsigned int i = 0; i < m_vViconVisualizers.size(); i++) {
		m_vViconVisualizers[i]->rewind();
	}
	
	// loop through all characters in band config file
	unsigned int index = 0;
	for (CharacterList::const_iterator it = c.begin();		
		it != c.end(); it++) {
		const CharacterConfig & character = *it;
		

		
		// update offset
		m_vCharacters[index].setPosition(character.getOffset().x, character.getOffset().y, character.getOffset().z);
		m_vViconVisualizers[index]->update(character.getDeltaTime());
		//update positions of the instrument associated
		/*Vector3 pos;
		if(stricmp(character.getName().c_str(), "Pineapple")
		{	
			pos = m_vCharacters[index].getEntity()->getParentSceneNode()->getPosition();
			pos.y = 0;
			pos.x += 30;
			pos.z += 30;
			mSceneMgr->getSceneNode("Percussion")->setPosition(pos);
		}*/

		index++;
		
		// update instrument position of applicable
		if (character.hasInstrument()) {
			Vector3 offset = character.getOffset() + character.getInstrumentOffset();
			std::cout << "Instrument offset: " << offset.x << ", " << offset.y << ", " << offset.z << endl;
			m_vCharacters[index].setPosition(offset.x, offset.y, offset.z);	
			m_vViconVisualizers[index]->update(character.getDeltaTime());
		
			index++;
		}
	}

	// rewind music
	if (BandConfigFile::hasSoundtrack()) {
		MusicPlayer::stopSound();
		MusicPlayer::playSound();
		
	}
	//fclose(fp);
}

void CheeseBurgerParadise::loadCharacter(const char * meshFile)
{
	// load character mesh and skeleton
	ViconCharacter vc(mSceneMgr, headNode);
	vc.createCharacter(meshFile);
	
	//scale hack
	
	if(0 == stricmp(meshFile, "Pineapple.mesh"))
	{
		vc.getEntity()->getParentNode()->scale(Vector3(1.3, 1.3, 1.3));
	}
	/*
	else if(0 == stricmp(meshFile, "RumRunner.mesh"))
		vc.getEntity()->getParentNode()->scale(Vector3(0.75, 0.75, 0.75));
	else if(0 == stricmp(meshFile, "Lizard.mesh"))
		vc.getEntity()->getParentNode()->scale(Vector3(1.0, 1.0, 1.0));
	*/
	// add character to list
	m_vCharacters.push_back(vc);

}
void CheeseBurgerParadise::loadMocapDataset(const char *subjectName, const char *vFile, const char *vskFile, float deltaTime)
{
	// loading the data set and the subject file
	ViconVVisualizer* vis = new ViconVVisualizer(mSceneMgr, headNode);
	vis->createScene(vFile);
	vis->getDataSet()->loadSubject(subjectName, vskFile);
	
	// offset deltaTime
	vis->update(deltaTime);
	
	// hide markers and bodies
	vis->setBodyVisible(false);
	vis->setMarkerVisible(false);
	
	// add dataset to vector so that it's updated
	m_vViconVisualizers.push_back(vis);


}


void CheeseBurgerParadise::connectCharacterWithData(const CharacterConfig & character, int charCount)
{
	//relate character to motion data 
	m_vCharacters[charCount].connectToMotionData(m_vViconVisualizers[charCount]->getDataSet());
	
	//also relate the map file
	//m_vCharacters[character].loadSkeletonMapping("C:\\Graphics_Home\\Mocap\\Datasets\\MotionViewerSampleData\\character\\plasticman\\sample1_to_plasticman.map");
	m_vCharacters[charCount].loadSkeletonMapping(character.getMapFile().c_str());

	//set data offsets particular to this character
	//m_vCharacters[charCount].setPosition(0,120,0);
	m_vCharacters[charCount].setPosition(character.getOffset().x,character.getOffset().y, character.getOffset().z);
}



void CheeseBurgerParadise::setShadows()
{
	m_vCharacters[0].getEntity()->setCastShadows(true);
}

//------------------------------------------------------------------------------
bool CheeseBurgerParadise::mouseMoved( const OIS::MouseEvent &arg )
{
	CEGUI::System::getSingleton().injectMouseMove( arg.state.X.rel, arg.state.Y.rel );
	
	static const float mul = 0.5;
	// rotation
	if (mShouldRotate) 
	{
		Quaternion deltaq;
		Real delta, dy, dx;
		Vector3 cmaRight, camDir;
		if (dy = arg.state.Y.rel) 
		{
			delta = dy * mul;
			deltaq.FromAngleAxis(Degree(delta), Vector3::UNIT_X);
			headNode->rotate(mCamera->getRight(), Degree(delta), Ogre::Node::TS_WORLD);
			//mTerrainRoot->rotate(mCamera->getRight(), Degree(delta), Ogre::Node::TS_WORLD);
		}

		if (dx = arg.state.X.rel) 
		{
			delta = dx * mul;
			deltaq.FromAngleAxis(Degree(delta), Vector3::UNIT_Y);
			headNode->rotate(Ogre::Vector3(0,1,0), Degree(delta), Ogre::Node::TS_LOCAL);
			//mTerrainRoot->rotate(Ogre::Vector3(0, 1, 0), Degree(delta), Ogre::Node::TS_LOCAL);
		}
	}
	// translation
	else if (mShouldTranslate) 
	{
		// move camera instead of root node
		mCamera->moveRelative(Vector3(-arg.state.X.rel*mul, arg.state.Y.rel*mul, 0));
	}
	// zoom
	else if (mShouldZoom)
	{
		// move camera instead of root node
		mCamera->moveRelative(Vector3(0, 0 , -arg.state.Y.rel*mul));
	}
	// another zoom for wheel
	else if (mAltDown && arg.state.Z.rel !=0)
	{
		// move camera instead of root node
		mCamera->moveRelative(Vector3(0, 0 , -arg.state.Z.rel*mul));
	}

	return true;
}

//------------------------------------------------------------------------------

bool CheeseBurgerParadise::frameStarted(const FrameEvent& evt) {

	cbpa->animateBird(mSceneMgr, evt);
	cbpa->animateStarFish(mSceneMgr, evt);
	return MotionViewerApp::frameStarted(evt);
}

